<?php
/**
 * Created by PhpStorm.
 * User: whwyy
 * Date: 2018/4/18 0018
 * Time: 10:40
 */

namespace Database;


use Yoc\Base\BObject;

class QueryBuilder extends BObject
{

    const UPDATE = 'update';
    const INSERT = 'insert';
    const COUNT = 'count';
    const EXISTS = 'exists';

    /** @var */
    public $column;


    /**
     * @param Model|\Db|Sql $query
     * @return string
     * @throws \Exception
     */
    public function builder($query)
    {
        if (empty($query->from)) {
            $query->from = $query->getModel()->getTable();
        }
        if (empty($query->select)) {
            $query->select = array_keys($query->getModel()->getColumn());
        }
        $builder = $this->builderSelect($query->select);
        $builder .= $this->builderFrom($query->from);
        $builder .= $this->builderAlias($query->alias);
        $builder .= $this->builderJoin($query->join);
        $builder .= $this->builderWhere($query->where);
        $builder .= $this->builderGroup($query->group);
        $builder .= $this->builderOrder($query->order);
        $builder .= $this->builderOffset($query->offset);
        $builder .= $this->builderLimit($query->limit);
        return $builder;
    }

    /**
     * @param Model|\Db $query
     * @return string
     * @throws \Exception
     */
    public function count($query)
    {
        if (empty($query->from)) {
            $query->from = $query->getModel()->getTable();
        }
        $builder = $this->builderSelect('COUNT(*)');
        $builder .= $this->builderFrom($query->from);
        $builder .= $this->builderAlias($query->alias);
        $builder .= $this->builderJoin($query->join);
        $builder .= $this->builderWhere($query->where);
        $builder .= $this->builderGroup($query->group);
        return $builder;
    }

    /**
     * @param Model $query
     * @return string
     * @throws \Exception
     */
    public function delete($query)
    {
        $builder = 'DELETE FROM ' . $query->getModel()->getTable();
        return $builder . $this->builderWhere($query->where);
    }

    /**
     * @param $table
     * @param $attributes
     * @param $condition
     * @param $params
     * @return array
     * @throws \Exception
     */
    public function update($table, $attributes, $condition, $params)
    {
        $sql = "UPDATE {$table} SET ";
        if (empty($params)) {
            throw new \Exception("");
        }
        $_tmp = [];
        foreach ($attributes as $val) {
            if (!isset($params[$val])) {
                continue;
            }
            $_tmp[] = $val . '=:' . $val;
        }
        if (!empty($_tmp)) {
            $sql .= implode(',', $_tmp) . ' WHERE ' . implode(' AND ', $condition);
        }

        return [$sql, $params];
    }

    /**
     * @param $table
     * @param $attributes
     * @param array|null $params
     * @return array
     * @throws \Exception
     */
    public function insert($table, $attributes, array $params = NULL)
    {
        $sql = "INSERT INTO {$table}(" . implode(',', $attributes) . ') VALUES(:' . implode(',:', $attributes) . ')';
        if (empty($params)) {
            throw new \Exception("");
        }
        foreach ($params as $key => $val) {
            if (strpos($sql, ':' . $key) === FALSE) {
                throw new \Exception("save data param not find.");
            }
        }
        return [$sql, $params];
    }


    /**
     * @param $table
     * @param $attributes
     * @param array|NULL $params
     * @return array
     * @throws \Exception
     */
    public function all($table, $attributes, array $params = NULL)
    {
        $sql = "INSERT INTO {$table}(" . implode(',', $attributes) . ') VALUES';
        if (empty($params)) {
            throw new \Exception("save data param not find.");
        }

        $insert = [];
        $insertData = [];

        foreach ($params as $key => $val) {
            if (!is_array($val)) {
                continue;
            }
            array_push($insert, '(:' . implode($key . ',:', $attributes) . $key . ')');

            foreach ($attributes as $myVal) {
                $insertData[':' . $myVal . $key] = $val[$myVal];
            }
        }

        if (empty($insertData) || empty($insert)) {
            throw new \Exception("save data is empty.");
        }

        $sql .= implode(',', $insert);

        return [$sql, $insertData];
    }

    /**
     * @param $table
     * @return string
     */
    public function getColumn($table)
    {
        return 'SHOW FULL FIELDS FROM ' . $table;
    }

    /**
     * @return string
     */
    private function builderSelect($select = NULL)
    {
        if (!empty($select)) {
            if (is_array($select)) {
                return "SELECT " . implode(',', $select);
            } else {
                return "SELECT " . $select;
            }
        } else {
            return "SELECT *";
        }
    }

    /**
     * @return string
     */
    private function builderAlias($alias)
    {
        return " AS " . $alias;
    }

    /**
     * @return string
     * @throws \Exception
     */
    private function builderFrom($table)
    {
        if (preg_match('/^\{\{.*\}\}$/', $table)) {
            $prefix = \Yoc::$app->db->tablePrefix;

            return " FROM " . $prefix . trim($table, '{{%}}');
        } else {
            return " FROM " . $table;
        }
    }

    /**
     * @return string
     */
    private function builderJoin($join)
    {
        return (empty($join) ? '' : ' ' . implode(' ', $join));
    }

    /**
     * @return string
     */
    private function builderWhere($where)
    {
        if (!empty($where)) {
            return ' WHERE ' . implode(' AND ', $where);
        } else {
            return '';
        }
    }

    /**
     * @return string
     */
    private function builderGroup($group)
    {
        if (!empty($group)) {
            return " GROUP BY $group";
        } else {
            return '';
        }
    }

    /**
     * @return string
     * @throws \Exception
     */
    private function builderOrder($order)
    {

        if (!empty($order)) {
            return ' ORDER BY ' . implode(',', $order);
        } else {
            return '';
        }
    }

    /**
     * @return string
     */
    private function builderOffset($offset)
    {
//		if (!empty($offset)) {
//			return ' OFFSET ' . $offset;
//		} else {
        return '';
//		}
    }

    /**
     * @return string
     */
    private function builderLimit($offset)
    {
        if (!empty($offset)) {
            return ' LIMIT ' . $offset;
        } else {
            return '';
        }
    }
}